package per.mapt.basic.nio;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

public class DataWriter {

    private Thread thread;
    private String name;

    private static class ChannelBuffer {
        private SocketChannel channel;
        private ByteBuffer buffer;

        public ChannelBuffer(SocketChannel channel, ByteBuffer buffer) {
            this.channel = channel;
            this.buffer = buffer;
        }
    }

    //这里认为selector可以快速注册，所以队列不需要很大
    private BlockingQueue<ChannelBuffer> toRegQuene = new LinkedBlockingDeque<>(128);
    private Selector selector;
    private volatile boolean running;

    public DataWriter(String name) throws IOException {
        selector = Selector.open();
        running = false;
        this.name = name;
    }

    public void start() {
        Runnable runnable = this::writingData;
        running = true;
        thread = new Thread(runnable);
        thread.start();
    }

    private void writingData() {
        while (running) {
            // 注册需要写入的channel
            // TODO 锁的位置不太了解，所以需要根据实际情况，把这个操作放到合适的位置
            while (!toRegQuene.isEmpty()) {
                ChannelBuffer chanBuffer = toRegQuene.poll();
                try {
                    chanBuffer.buffer.flip();
                    chanBuffer.channel.write(chanBuffer.buffer);
                    if (chanBuffer.buffer.hasRemaining()) {
                        //TODO 忘了多个buffer的情况了。。。
                        chanBuffer.channel.register(selector, SelectionKey.OP_WRITE, chanBuffer.buffer);
                    }else{
//                        chanBuffer.channel.
                    }
                } catch (ClosedChannelException e) {
                    // TODO 需要回调
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                if (selector.select(1000) == 0) {
                    continue;
                }
            } catch (IOException e) {
                //TODO 处理方式需要细化
                e.printStackTrace();
                break;
            }
            for (Iterator<SelectionKey> iterator = selector.selectedKeys().iterator(); iterator.hasNext(); ) {
                SelectionKey key = iterator.next();
                iterator.remove();
                SocketChannel channel = (SocketChannel) key.channel();
                if (key.isWritable()) {
                    ByteBuffer buffer = (ByteBuffer) key.attachment();
                    buffer.flip();
                    try {
                        channel.write(buffer);
                        if (!buffer.hasRemaining()) {
                            // 如果buffer没有数据需要写入了，那么就不需要了在监听可写事件了
                            //TODO 忘了多个buffer的情况了。。。
                            key.cancel();
                        }
                        buffer.compact();
                    } catch (IOException e) {
                        // TODO 需要回调
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    // 待向selector注册的队列
    public boolean writeData(SocketChannel channel, ByteBuffer buffer) {
        boolean ret = toRegQuene.offer(new ChannelBuffer(channel, buffer));
        // 需要唤醒selector
        selector.wakeup();
        return ret;
    }

    public synchronized void close() throws IOException {
        if (running) {
            // 这里只关闭channel与selector的关联，不处理channel的关闭
            running = false;
            selector.close();
        }
    }
}
